package main

import (
	"bufio"
	//"database/sql"
	"fmt"
	"github.com/buaazp/fasthttprouter"
	//_ "github.com/go-sql-driver/mysql"
	"bytes"
	"github.com/deckarep/golang-set"
	"github.com/valyala/fasthttp"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	//"strings"
	"time"
)

const (
	last_op_num = 20
	//timelayout = "2006-01-02 15:04:05"
	timelayout  = "20060102"
	timelayout2 = "2006-01-02"
)

var (
	log_dir        = "/home/jl/KK_Log/INFO/"
	config_last_op = false
	count1         = make(map[int]int64)
	last_10_op     = make(map[string][]int)
	cencerned_op   = map[int]bool{
		1: true,
		2: true,
	}
	map_oneday_op_whom = make(map[int][]string)
	map_date_op        = make(map[string]map[int][]string)
)

func F_rootPage(ctx *fasthttp.RequestCtx) {
	fmt.Fprint(ctx, "NOT Welcome!\n")

	fmt.Fprintf(ctx, "Request method is %q\n", ctx.Method())
	fmt.Fprintf(ctx, "RequestURI is %q\n", ctx.RequestURI())
	fmt.Fprintf(ctx, "Requested path is %q\n", ctx.Path())
	fmt.Fprintf(ctx, "Host is %q\n", ctx.Host())
	fmt.Fprintf(ctx, "Query string is %q\n", ctx.QueryArgs())
	fmt.Fprintf(ctx, "User-Agent is %q\n", ctx.UserAgent())
	fmt.Fprintf(ctx, "Connection has been established at %s\n", ctx.ConnTime())
	fmt.Fprintf(ctx, "Request has been started at %s\n", ctx.Time())
	fmt.Fprintf(ctx, "Serial request number for the current connection is %d\n", ctx.ConnRequestNum())
	fmt.Fprintf(ctx, "Your ip is %q\n\n", ctx.RemoteIP())

	fmt.Fprintf(ctx, "Raw request is:\n---CUT---\n%s\n---CUT---", &ctx.Request)

	ctx.SetContentType("text/plain; charset=utf8")

	// Set arbitrary headers
	ctx.Response.Header.Set("X-My-Header", "my-header-value")

	// Set cookies
	var c fasthttp.Cookie
	c.SetKey("cookie-name")
	c.SetValue("cookie-value")
	ctx.Response.Header.SetCookie(&c)
}

func F_init(ctx *fasthttp.RequestCtx) {
	//count1 = make(map[string]int64) // use := will cause an error

	a_filemeta, err := ioutil.ReadDir(log_dir)
	if nil != err {
		fmt.Fprintf(ctx, "error: %v\n", err)
	}

	for _, file := range a_filemeta {
		fmt.Fprintf(ctx, "process  %q\n", file.Name())
		//fmt.Println(file.Name())

		err := _parse_a_file(log_dir+file.Name(), ctx)
		if nil != err {
			fmt.Fprintf(ctx, "error: %v\n", err)
		}
		for k, v := range count1 {
			fmt.Fprintf(ctx, "OP %d  -->  %d times\n", k, v)
		}
	}
}

func _load_day(day string, ctx *fasthttp.RequestCtx) {
	// clean up for a whole day
	map_oneday_op_whom = make(map[int][]string)

	count_has_log := 0
	for i := 0; i < 24; i++ {
		fname := fmt.Sprintf("%s%02d", day, i)
		fname = log_dir + "INFO_" + fname + ".txt"
		//fmt.Fprintf(ctx, "%s\n", fname)

		err := _parse_a_file(fname, ctx)
		if nil != err {
			fmt.Fprintf(ctx, "error: %v\n", err)
		} else {
			count_has_log++
		}
	}

	// save day
	if count_has_log > 0 {
		map_date_op[day] = map_oneday_op_whom
	}
	map_oneday_op_whom = nil
}

func F_loadday(ctx *fasthttp.RequestCtx) {
	day, _ := ctx.UserValue("day").(string)
	_load_day(day, ctx)

	fmt.Fprintf(ctx, "Day %s loaded.\n", day)
}

func F_load_range(ctx *fasthttp.RequestCtx) {
	day, _ := ctx.UserValue("day").(string)

	date0, err := time.Parse(timelayout, day)
	if nil != err {
		fmt.Fprintf(ctx, "error: invalid day\n")
		return
	}

	today := time.Now()

	for i := 0; i < 10000; i++ {
		datex := date0.AddDate(0, 0, i)
		if today.Before(datex) {
			break
		}

		ss := datex.Format(timelayout)
		_load_day(ss, ctx)
		fmt.Fprintf(ctx, "Day %s loaded.\n", ss)
	}
}

func _parse_a_file(fname string, ctx *fasthttp.RequestCtx) error {
	nobuf, err := os.Open(fname)
	if nil != err {
		return err
	}
	defer nobuf.Close()

	//out2 := make([][]byte, 0, 16)
	//out1 := make([][][]byte, 0, 16)

	bufed := bufio.NewReader(nobuf)
	var last_line []byte
	//var line_reader *bufio.Reader
	for {
		line, err := bufed.ReadBytes('\n')
		if nil != err {
			break
		}
		line = line[0 : len(line)-1]
		// fix there is a '\n' in username
		if '|' == line[0] {
			last_line = append(last_line, line...)
			line = nil
		}

		//fmt.Print(string(line))

		if nil != last_line {
			/*
				if nil == line_reader {
					line_reader = bufio.NewReader(bytes.NewReader(last_line))
				} else {
					line_reader.Reset(bytes.NewReader(last_line)) // reuse it
				}
			*/
			_parse_a_line(last_line, ctx)
		}

		last_line = line
	}
	//line_reader.Reset(bytes.NewReader(last_line)) // reuse it
	_parse_a_line(last_line, ctx)

	// trip slice of last 10 OPs
	for k, v := range last_10_op {
		ll := len(v)
		if ll > last_op_num {
			last_10_op[k] = v[ll-last_op_num : ll]
		}
	}

	return nil
}

func _parse_a_line(line []byte, ctx *fasthttp.RequestCtx) {
	/*
		slice1 := make([][]byte, 0, 10)
		for {
			aa, err := line_reader.ReadBytes('|')
			if nil != err {
				break
			}
			slice1 = append(slice1, aa[0:len(aa)-1])
		}
	*/
	delim := []byte{'|'}
	slice1 := bytes.Split(line, delim)
	if len(slice1) > 0 {
		//fmt.Print("%q  %q\n", slice1[0], slice1[1])
		op_code, _ := strconv.Atoi(string(slice1[1]))
		//op_code := string(slice1[1])
		// ================================ BEGIN
		count1[op_code] = count1[op_code] + 1

		userid := string(slice1[2])
		if config_last_op {
			slice3 := last_10_op[userid]
			last_10_op[userid] = append(slice3, op_code)
		}

		if true == cencerned_op[op_code] {
			sl1 := map_oneday_op_whom[op_code]
			map_oneday_op_whom[op_code] = append(sl1, userid)
		}
		// ================================ END
	}
}

func F_last_ops_with_id(ctx *fasthttp.RequestCtx) {
	aa, _ := ctx.UserValue("id").(string)
	id1 := aa + ":Egls"
	sl := last_10_op[id1]
	if 0 == len(sl) {
		fmt.Fprintf(ctx, "id %q NOT found\n", id1)
	} else {
		for _, v := range sl {
			fmt.Fprintf(ctx, "%d  ", v)
		}
		fmt.Fprintf(ctx, "\n")
	}
}

func F_newaccount(ctx *fasthttp.RequestCtx) {
	day, _ := ctx.UserValue("day").(string)
	aa, _ := ctx.UserValue("num").(string)
	num_to_run, _ := strconv.Atoi(aa)

	date0, err := time.Parse(timelayout, day)
	if nil != err {
		fmt.Fprintf(ctx, "error: invalid day\n")
		return
	}

	today := time.Now()
	os1 := ""
	os2 := ""

	for i := 0; i < num_to_run; i++ {
		datex := date0.AddDate(0, 0, i)
		if today.Before(datex) {
			break
		}

		ss := datex.Format(timelayout)
		os1 = os1 + fmt.Sprintf("%s\t", datex.Format(timelayout2))

		m, has := map_date_op[ss]
		if false == has {
			os2 = os2 + "NA\t"
		} else {
			sl1 := m[1]
			set1 := mapset.NewThreadUnsafeSet()
			for _, v := range sl1 {
				set1.Add(v)
			}

			os2 = os2 + fmt.Sprintf("%d\t", set1.Cardinality())
		}
	}
	fmt.Fprintln(ctx, os1)
	fmt.Fprintln(ctx, os2)
}

func F_liucun(ctx *fasthttp.RequestCtx) {
	day, _ := ctx.UserValue("day").(string)
	aa, _ := ctx.UserValue("num").(string)
	num_to_run, _ := strconv.Atoi(aa)

	m, has := map_date_op[day]
	if false == has {
		fmt.Fprintf(ctx, "Data of %s have NOT loaded\n", day)
	} else {
		date0, err := time.Parse(timelayout, day)
		if nil != err {
			fmt.Fprintf(ctx, "error: invalid day\n")
			return
		}

		sl1 := m[1] // 1 is new account
		set1 := mapset.NewThreadUnsafeSet()
		for _, v := range sl1 {
			set1.Add(v)
		}
		date0_num := set1.Cardinality()
		fmt.Fprintf(ctx, "date0 xinzen: %d\n", date0_num)

		today := time.Now()

		a_num := make([]int, 0, 100)
		a_hasdata := make([]bool, 0, 100)

		for i := 1; i <= num_to_run; i++ {
			datex := date0.AddDate(0, 0, i)
			if today.Before(datex) {
				break
			}

			ss := datex.Format(timelayout)
			//fmt.Fprintf(ctx, "%q\n", ss)

			d, has := map_date_op[ss]
			if false == has {
				//fmt.Fprintf(ctx, "wanring: dat %s has NO data\n", ss)
				a_hasdata = append(a_hasdata, false)
				a_num = append(a_num, 0)
			} else {
				a_hasdata = append(a_hasdata, true)

				sl2 := d[2] // 2 is login
				set2 := mapset.NewThreadUnsafeSet()
				for _, v := range sl2 {
					set2.Add(v)
				}

				set3 := set1.Intersect(set2)
				n := set3.Cardinality()

				a_num = append(a_num, n)
				//fmt.Fprintf(ctx, "After day %d: left %d, %f per\n", i, n, (float32(n) / float32(date0_num)))
			}
		}

		header := ""
		line1 := ""
		line2 := ""
		for ind, has := range a_hasdata {
			header = header + fmt.Sprintf("第%d天\t", ind+1)
			if false == has {
				line2 = line2 + "NA\t"
				line1 = line1 + "NA\t"
			} else {
				line2 = line2 + fmt.Sprintf("%d\t", a_num[ind])
				line1 = line1 + fmt.Sprintf("%.2f\t", float32(a_num[ind])*float32(100)/float32(date0_num))
			}
		}

		fmt.Fprintln(ctx, header)
		fmt.Fprintln(ctx, line1)
		fmt.Fprintln(ctx, line2)
	}
}

func main() {
	date0, _ := time.Parse(timelayout, "20161101")
	fmt.Println(date0)

	our_router := fasthttprouter.New()

	//our_router.GET("/", F_rootPage)
	//our_router.GET("/api/v1/init", F_init)
	our_router.GET("/api/v1/lastopwithid/:id", F_last_ops_with_id)
	our_router.GET("/api/v1/loadday/:day", F_loadday)
	our_router.GET("/api/v1/loadrange/:day", F_load_range)
	our_router.GET("/api/v1/xinzen/:day/:num", F_newaccount)
	our_router.GET("/api/v1/liucun/:day/:num", F_liucun)

	log.Fatal(fasthttp.ListenAndServe(":5777", our_router.Handler))
}
